package com.example.calculation;


import com.example.collection.ObjectPool;
import com.example.collection.TopKList;

import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

/**
 * ChefAndRecipeThread类用于计算 厨师和菜谱组合后，得分情况
 */

final public class ChefAndRecipeThread implements Callable<List<TopResult>>{

    //返回的数据量
    private final static int tomNum = 50000;

    //菜谱组合的排列结果
    int[][] playRecipes;
    //将有序的菜谱排列（precipes），  生成其所有的无序排列情况
    static int[][] disordePermuation = new int[1680][9];

    static {
        int[] needPermuation = {0, 1, 2, 3, 4, 5, 6, 7, 8};
        // 调用后 disordePermuation2中为排序结果
        permute(needPermuation, new int[9], new int[3], 0);
    }


    /**
     * 上场厨师的推理结果 （至少3星） (这个可以储存起来)，
     * playChefs是多个线程共享的数据，因此只适合读取，不因该做修改，
     */
    int[][] playChefs;

    //TopResult得分比较对象，用于从高到底排列得分
    Comparator comparator = new Comparator<TopResult>(){
        @Override
        public int compare(TopResult o1, TopResult o2) {
            return o2.score - o1.score;
        }
    };

    /**
     * 菜谱排列结果在一个数组中，为了多线程计算，
     * 将菜谱拆分为了很多段，每一段根据
     */
    // 分段后 ，本线程负责部分 的开始位置
    final private int start;

    // 负责部分的  结束位置 ，limit肯定不比start小
    final private int limit;

    //TopResult对象池
    ObjectPool<TopResult> objectPool;


    /**
     * 在一个线程执行完之后，会更改minMax的值， 每一个call方法执行前，会获取一次其值
     */
    AtomicInteger globalMinMax;

    /**
     * [厨师id][CalRecipe.index]  ,   每一个CalRecipe.index包含了菜谱和数量信息
     */
    int[][] scoreCache = null;

    public ChefAndRecipeThread(int[][] playRecipes, int[][] playChefs, int[][] scoreCache, int start, int limit, AtomicInteger globalMinMax) {
        this.playRecipes = playRecipes;
        this.playChefs = playChefs;
        this.scoreCache = scoreCache;
        this.start = start;
        this.limit = limit;
        this.globalMinMax = globalMinMax;

        // 初始化对象池
        int length = tomNum + 20000;

        objectPool = new ObjectPool<TopResult>(length);

        for (int i = 0; i < length; i++) {
            objectPool.addObject(new TopResult(null, null, 0));
        }
    }

    /**
     * @return 返回得分最高的tomNum个结果，结果是有序的，已经按照得分从高到底排序了
     */
    @Override
    public List<TopResult> call() {
        try {
            //用于统计执行时间
            long starttime = 0, endtime = 0;
            // minMax是topPlayChers集合中，最低的那个得分
            int minMax = globalMinMax.get();


            starttime = System.currentTimeMillis();

            // 返回集，保存得分最高的数据集合
            TopKList topPlayChers;

            topPlayChers = new TopKList(tomNum + 10000, tomNum);

            int[] recipes = new int[9];
            int cal = 0;
            //遍历playRecipes中，本线程负责的部分
            for (int i = start; i < limit; i++) {
                int[] precipes = playRecipes[i];

                //disordePermuation长度是1680 固定的
                for (int k = 0; k < 1680; k++) {
                    int[] ints = disordePermuation[k];
                    recipes[0] = precipes[ints[0]];
                    recipes[1] = precipes[ints[1]];
                    recipes[2] = precipes[ints[2]];
                    recipes[3] = precipes[ints[3]];
                    recipes[4] = precipes[ints[4]];
                    recipes[5] = precipes[ints[5]];
                    recipes[6] = precipes[ints[6]];
                    recipes[7] = precipes[ints[7]];
                    recipes[8] = precipes[ints[8]];

                    for (int i2 = 0; i2 < playChefs.length; i2++) {
                        int[] chefs = playChefs[i2];
                        //无厨具情况下 允许厨师有一项技法不满足， 按照可品质计算
                        cal = CacheSoore(chefs, recipes);
                        //得分比minMax 高，则应该将计算结果放入
                        if (cal > minMax) {
                            //从对象池中获取一个对象， 这里之所以用对象池，是为了减少对象创建，减少gc
                            TopResult topResult = objectPool.borrowObject();

                            //更新对象的信息， 相当于重新创建对象
                            topResult.update(chefs, recipes.clone(), cal);

                            //向结果集中添加一条记录，
                            // 返回是否重新排序了
                            if (!topPlayChers.add(topResult)) {
                                objectPool.returnObject(topResult);
                            }
                            if (topPlayChers.removes.size() > 0) {
                                for (TopResult remove : topPlayChers.removes) {
                                    objectPool.returnObject(remove);
                                }
                                topPlayChers.removes.clear();
                            }
                        }
                    }
                }
            }

            ArrayList<TopResult> topK = new ArrayList<>(topPlayChers.size() + 1);
            topPlayChers.sortImmediately();

            for (int i = 0; i < topPlayChers.size(); i++) {
                topK.add(topPlayChers.get(i));
            }

            Collections.sort(topK, comparator);
            if (topK.size() > tomNum) {
                topK.subList(tomNum, topPlayChers.size()).clear();
            }

            endtime = System.currentTimeMillis();
            System.out.println((limit - start) + "组菜谱,全厨师排列结果用时:" + (endtime - starttime) + "ms");
            return topK;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     *
     */
    final private int CacheSoore(int[] chefIndex, int[] recipes) {
        int score = 0;
        int[] recipeScore;
        //第一个厨师
        recipeScore = scoreCache[chefIndex[0]];
        score += recipeScore[recipes[0]];
        score += recipeScore[recipes[1]];
        score += recipeScore[recipes[2]];

        //第二个厨师
        recipeScore = scoreCache[chefIndex[1]];
        score += recipeScore[recipes[3]];
        score += recipeScore[recipes[4]];
        score += recipeScore[recipes[5]];

        //第三个厨师
        recipeScore = scoreCache[chefIndex[2]];
        score += recipeScore[recipes[6]];
        score += recipeScore[recipes[7]];
        score += recipeScore[recipes[8]];
        return score;
    }


    /**
     * 将一组有序的菜谱排列，  生成其所有的无序排列情况
     * 这里的排序可以理解为，有三个桶，每个桶中可以放三个元素（桶中元素不考虑顺序）， 计算有多少种放置方法
     *
     * @param playress 有序的菜谱排列
     * @param start    当前排列的元素（0-8）
     * @param count    记录一组无序排列情况中，排列元素个数
     */


    static int index = 0;

    final static private void permute(int[] need, int[] tmp, int[] count, int start) {
        if ((count[0] + count[1] + count[2]) == 9) {
            for (int i = 0; i < 9; i++) {
                disordePermuation[index][i] = tmp[i];
            }
            index++;
            return;
        }
        for (int j = 0; j < 3; j++) {
            if (count[j] == 3) {
                continue;
            }
            tmp[(j * 3) + count[j]] = need[start];
            count[j]++;
            permute(need, tmp, count, start + 1);
            count[j]--;
        }
    }


}
